using System;
using SunflowSharp.Image;
using SunflowSharp.Maths;

namespace SunflowSharp.Core
{

    /**
     * Represents a sample taken from a light source that faces a point being
     * shaded.
     */
    public class LightSample
    {
        private Ray shadowRay; // ray to be used to evaluate if the point is in
        // shadow
        private Color ldiff;
        private Color lspec;
        public LightSample next; // pointer to next item in a linked list of samples

        /**
         * Creates a new light sample object (invalid by default).
         */
        public LightSample()
        {
            ldiff = lspec = null;
            shadowRay = null;
            next = null;
        }

        bool isValid()
        {
            return ldiff != null && lspec != null && shadowRay != null;
        }

        /**
         * Set the current shadow ray. The ray's direction is used as the sample's
         * orientation.
         * 
         * @param shadowRay shadow ray from the point being shaded towards the light
         */
        public void setShadowRay(Ray shadowRay)
        {
            this.shadowRay = shadowRay;
        }

        /**
         * Trace the shadow ray, attenuating the sample's color by the opacity of
         * intersected objects.
         * 
         * @param state shading state representing the point to be shaded
         */
        public void traceShadow(ShadingState state)
        {
            Color opacity = state.traceShadow(shadowRay);
            Color.blend(ldiff, Color.BLACK, opacity, ldiff);
            Color.blend(lspec, Color.BLACK, opacity, lspec);
        }

        /**
         * Get the sample's shadow ray.
         * 
         * @return shadow ray
         */
        public Ray getShadowRay()
        {
            return shadowRay;
        }

        /**
         * Get diffuse radiance.
         * 
         * @return diffuse radiance
         */
        public Color getDiffuseRadiance()
        {
            return ldiff;
        }

        /**
         * Get specular radiance.
         * 
         * @return specular radiance
         */
        public Color getSpecularRadiance()
        {
            return lspec;
        }

        /**
         * Set the diffuse and specular radiance emitted by the current light
         * source. These should usually be the same, but are distinguished to allow
         * for non-physical light setups or light source types which compute diffuse
         * and specular responses seperately.
         * 
         * @param d diffuse radiance
         * @param s specular radiance
         */
        public void setRadiance(Color d, Color s)
        {
            ldiff = d.copy();
            lspec = s.copy();
        }

        /**
         * Compute a dot product between the current shadow ray direction and the
         * specified vector.
         * 
         * @param v direction vector
         * @return dot product of the vector with the shadow ray direction
         */
        public float dot(Vector3 v)
        {
            return shadowRay.dot(v);
        }
    }
}